package ge.modules.ia;

import ge.engine.Actor;
import ge.modules.Module;

import java.util.Stack;

/**
 *
 * Implements a StateMachine. It has one global state which is executed before the current state,
 * in order to have a control state for common stuff
 * 
 * The class also implements a deliver messages system who makes possible communication with all the
 * states
 * @author abel.botrdonado	
 *	
 */
public class StateMachineModule<T extends Actor> implements Module 
{

	private T owner;
	private IMachineState<T> globalState,currentState;
	private Stack<IMachineState<T>> stack;
	private int id;
	/**W
	 * 	Constructor
	 * @param owner The owner of the state machine, NULL if there isn't one
	 */
	public StateMachineModule(int id, T owner)
	{
		this.owner = owner;
		StateMachineBaseEntity.get().suscribe(id, this);
	}
	/**
	 * 	Sets the global state of the machine
	 * @param globalState New global state
	 */
	public void setGlobalState(IMachineState<T> state)
	{
		if(globalState == state)
			return;
		if(globalState != null)
			globalState.exit(owner);
		
		globalState = state;
		
		if(globalState != null)
			globalState.enter(owner);
	}
	
	/**
	 * 
	 * @return The current global state
	 */
	public IMachineState<T> getGlobalState()
	{
		return globalState;
	}
	/**
	 * 
	 * @return The current state
	 */
	public IMachineState<T> getCurrentState()
	{
		return currentState;
	}
	
	/**
	 *  Stares current state in the stack. It still is the current state
	 */
	public void pushState()
	{
		stack.add(currentState);
	}
	/**
	 * 
	 * @return The size of the stack
	 */
	public int stackLength()
	{
		return stack.size();
	}
	/**
	 * @return True if the stack is empty
	 */
	public boolean isStackEmpty()
	{
		return stack.isEmpty();
	}
	/**
	 * Restores the head state in the stack. 
	 * It does nothing if the stack is empty 
	 */
	public void popState()
	{
		IMachineState<T> storedState = stack.pop();
		
		changeState(storedState);
	}
	
	/**
	 * 
	 * @param state The state to be asked
	 * @return True if that state is the current state
	 */
	public boolean isInState(IMachineState<T> state)
	{
		return state == currentState;
	}
	/**
	 * Changes the current state for the new one
	 * @param state New State
	 */
	public void changeState(IMachineState<T> state)
	{
		if(currentState == state)
			return;
		if(currentState != null)
			currentState.exit(owner);
		
		currentState = state;
		
		if(currentState != null)
			currentState.enter(owner);
	}
	@Override
	public void update() 
	{
		if(globalState != null)
			globalState.execute(owner);
		
		if(currentState != null)
			currentState.execute(owner);

	}
	
	/**
	 * Does nothing
	 */
	@Override
	public void draw() {


	}
	/**
	 * 	Handles the messages send by the MessageDispatcher
	 * 	First try to handle with the global state, and if fail it will try whith the current
	 * state. 
	 * @param m Message
	 * @return True if one of the two states has handled the message
	 */
	public boolean handleMessage(Message m)
	{
		if(globalState != null && globalState.handleMessage(owner,m))
			return true;
		
		if(currentState != null && currentState.handleMessage(owner,m))
			return true;
		
		return false;	
	}

	@Override
	public void delete() {
		 StateMachineBaseEntity.get().remove(id);

	}

}
